<?php
namespace DRY\Document;

/**
 * The "File" model represents a simple file stored in an arbitrary folder (set in config).
 * Provides functionality to create such files, read, update or delete them ( CRUD )
 *
 * @author Wavetrex <wavetrex@gmail.com>
 * @link http://www.donrey.net/Documentation/Structure/Modules/document.html
 * @license [GPL v3] http://www.gnu.org/licenses/gpl-3.0.html
 * @version 1.0.dev
 * @package DonReY
 * @subpackage Module.Document
 */
class Model_File extends Model_base
{
	/**
	 * Name of the file that represents the document
	 * @var string
	 */
	protected $filename;

	protected $attr_filename;

	/**
	 * Serializer module used for attributes packing/unpacking before/after write/read
	 * @var \DRY\Module\Serializer
	 */
	protected $attr_serializer;

	/**
	 * Document model constructor
	 * @param \DRY\Module\Document $document_module
	 * @param \SimpleXMLElement $CFG
	 * @param string $id
	 */
	public function __construct(\DRY\Module\Document $document_module, $CFG, $id = null)
	{
		$paths = \DRY\core::assemble_Absolute_Paths($CFG-> storage);
		$this-> filename = $paths['path'][0].'/'.$id;

		// attributes
		if(isset($CFG-> attr)) {
			$this-> attr_filename = $this-> filename.($CFG-> attr-> extension?:'.metadata');
			$this-> attr_serializer = $document_module['serializer'];
		}
	}

	public function __destruct()
	{
		if($this-> attr_changed)
			// Packs and writes attributes in the companion file
			file_put_contents($this-> attr_filename, $this-> attr_serializer-> serialize($this-> attributes));
	}

	/**
	 * Reads and returns the document file content
	 */
	public function read()
	{
		if(file_exists($this-> filename))
			return file_get_contents($this-> filename);
		else
			return '';
	}

	/**
	 * Creates or updates a document file in the storage directory
	 * @param string $content
	 */
	public function write($content = '')
	{
		file_put_contents($this-> filename, $content);
	}

	/**
	 * Removes the document file (and it's metadata)
	 */
	public function destroy()
	{
		unlink($this-> filename);
		if(!empty($this-> attr_filename))
			unlink($this-> attr_filename);
	}

	/**
	 * Attributes associated with the document
	 * @var array
	 */
	protected $attributes = array();

	/**
	 * Have attributes been loaded?
	 * @var bool
	 */
	protected $attr_loaded = false;

	/**
	 * Has any attribute been changed
	 * @var bool
	 */
	protected $attr_changed = false;

	/**
	 * Reads all attributes from the special attributes companion file ( usually, {filename}.metadata )
	 */
	protected function read_Attributes()
	{
		if(file_exists($this-> attr_filename)) {
			$this-> attributes = $this-> attr_serializer-> unserialize(file_get_contents($this-> attr_filename));
		} else
			$this-> attributes = array();

		$this-> attr_loaded = true;
	}

	public function __get($attr)
	{
		if(empty($this-> attr_filename))
			return null;

		if(!$this-> attr_loaded)
			$this-> read_Attributes();

		if(isset($this-> attributes[$attr]))
			return $this-> attributes[$attr];
	}

	/**
	 * Sets an attribute
	 * @param string $attr
	 * @param mixed $value
	 */
	public function __set($attr, $value)
	{
		if(empty($this-> attr_filename))
			return;

		if(!$this-> attr_loaded)
			$this-> read_Attributes();

		$this-> attributes[$attr] = $value;
		$this-> attr_changed = true;
	}

	public function __isset($attr)
	{
		if(empty($this-> attr_filename))
			return false;

		if(!$this-> attr_loaded)
			$this-> read_Attributes();

		return isset($this-> attributes[$attr]);
	}

	public function __unset($attr)
	{
		if(empty($this-> attr_filename))
			return;

		if(!$this-> attr_loaded)
			$this-> read_Attributes();

		unset($this-> attributes[$attr]);
		$this-> attr_changed = true;
	}

	/**
	 * Searches all metadata files for a certain attribute and returns the document $id ( base filename )
	 * Note: This is slow for large number of documents files, because there is no index and all files have to be searched sequentially!
	 *
	 * @param \DRY\Module\Document $document_module
	 * @param \SimpleXMLElement $CFG
	 * @param string $attr
	 * @param mixed $value
	 * @param string $extension
	 */
	public static function search_Attribute(\DRY\Module\Document $document_module, $CFG, $attr, $value)
	{
		if(!isset($CFG-> attr))
			return null; // error, cannot search when attributes feature is disabled

		$paths = \DRY\core::assemble_Absolute_Paths($CFG-> storage);
		$extension = $CFG-> attr-> extension?:'.metadata'; $ext_len = strlen($extension);

		try {
			// walk the dir
			foreach (new \DirectoryIterator($paths['path'][0]) as $entry) {
				if($entry-> isFile() && substr($entry-> getFilename(), -$ext_len)==$extension) {

					$attrs = $document_module['serializer']-> unserialize(file_get_contents($entry-> getPathname()));
					if(isset($attrs[$attr]) && $attrs[$attr]==$value) {
						// found !
						return new self($document_module, $CFG, $entry-> getBasename($extension));
					}
				}
			}
		} catch (\Exception $e) {
			// TODO: Better errors
//			echo $e->getMessage();
			return null; // error, bad path?
		}
	}

}
